package com.itheima.security.distributed.order.aspect;

import com.itheima.security.distributed.order.annotatioin.PreAuth;
import com.itheima.security.distributed.order.common.ClassUtil;
import com.itheima.security.distributed.order.common.PreAuthFun;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.core.MethodParameter;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * @author: yangguoxiong
 * @date: 2022/3/9 17:29
 * @description:
 */
@Aspect
@Component
public class PreAuthAspect implements ApplicationContextAware {
    private ApplicationContext applicationContext;
    private static final ExpressionParser EXPRESSION_PARSER = new SpelExpressionParser();

    public PreAuthAspect() {
    }

    @Around("@annotation(com.itheima.security.distributed.order.annotatioin.PreAuth) || @within(com.itheima.security.distributed.order.annotatioin.PreAuth)")
    public Object preAuth(ProceedingJoinPoint point) throws Throwable {
        if (this.handleAuth(point)) {
            return point.proceed();
        } else {
            throw new RuntimeException("权限不足");
        }
    }

    private boolean handleAuth(ProceedingJoinPoint point) {
        MethodSignature methodSignature = (MethodSignature) point.getSignature();
        Method method = methodSignature.getMethod();
        PreAuth preAuth = (PreAuth) ClassUtil.getAnnotation(method, PreAuth.class);
        String condition = preAuth.value();
        if (StringUtils.isNotBlank(condition)) {
            Expression expression = EXPRESSION_PARSER.parseExpression(condition);
            Object[] args = point.getArgs();
            StandardEvaluationContext context = this.getEvaluationContext(method, args);
            return (Boolean) expression.getValue(context, Boolean.class);
        } else {
            return false;
        }
    }

    private StandardEvaluationContext getEvaluationContext(Method method, Object[] args) {
        StandardEvaluationContext context = new StandardEvaluationContext(new PreAuthFun());
        context.setBeanResolver(new BeanFactoryResolver(this.applicationContext));

        for (int i = 0; i < args.length; ++i) {
            MethodParameter methodParam = ClassUtil.getMethodParameter(method, i);
            context.setVariable(methodParam.getParameterName(), args[i]);
        }

        return context;
    }

    public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}
